//+------------------------------------------------------------------+
//|                                                  KDJ(3Lines).mq5 |
//|                                           Copyright 2020,fxMeter |
//|                            https://www.mql5.com/zh/users/fxmeter |
//+------------------------------------------------------------------+
//2020-2-15 
//MT4 版本 https://www.mql5.com/zh/code/27946
//MT5 版本 https://www.mql5.com/zh/code/27947
/* 通达信公式改编
RSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100;
K:SMA(RSV,M1,1);
D:SMA(K,M2,1);
J:3*K-2*D;
1.指标>80时，回档机率大；指标<20时，反弹机率大；
2.K在20左右向上交叉D时，视为买进信号；
3.K在80左右向下交叉D时，视为卖出信号；
4.J>100时，股价易反转下跌；J<0时，股价易反转上涨；
5.KDJ波动于50左右的任何信号，其作用不大。
*/
#property copyright "Copyright 2020,fxMeter"
#property link      "https://www.mql5.com/zh/users/fxmeter"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 11
#property indicator_plots   5
//--- plot K (动态颜色线)
#property indicator_label1  "K"
#property indicator_type1   DRAW_COLOR_LINE
#property indicator_color1  clrWhite,clrRed,clrLime
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2
//--- plot D (动态颜色线)
#property indicator_label2  "D"
#property indicator_type2   DRAW_COLOR_LINE
#property indicator_color2  clrGold,clrRed,clrLime
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2
//--- plot J (动态颜色线)
#property indicator_label3  "J"
#property indicator_type3   DRAW_COLOR_LINE
#property indicator_color3  clrDarkViolet,clrRed,clrLime
#property indicator_style3  STYLE_SOLID
#property indicator_width3  2
//--- plot Level80 (动态80水平线)
#property indicator_label4  "Level80"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrOrange
#property indicator_style4  STYLE_SOLID
#property indicator_width4  2
//--- plot Level20 (动态20水平线)
#property indicator_label5  "Level20"
#property indicator_type5   DRAW_LINE
#property indicator_color5  clrCyan
#property indicator_style5  STYLE_SOLID
#property indicator_width5  2

#property indicator_level1  0
#property indicator_level2  20
#property indicator_level3  50
#property indicator_level4  80
#property indicator_level5  100

//---- input parameters
input int N =45;           // KDJ周期参数
input int M1=9;            // K线平滑参数
input int M2=3;            // D线平滑参数
input double OverBought=80.0;  // 超买阈值（默认80）
input double OverSold=20.0;    // 超卖阈值（默认20）
//--- indicator buffers
double         KBuffer[];        // K线数值缓冲区
double         KColorBuffer[];   // K线颜色缓冲区
double         DBuffer[];        // D线数值缓冲区
double         DColorBuffer[];   // D线颜色缓冲区
double         JBuffer[];        // J线数值缓冲区
double         JColorBuffer[];   // J线颜色缓冲区
double         Level80Buffer[];  // 动态80水平线缓冲区
double         Level20Buffer[];  // 动态20水平线缓冲区
double llv[],hhv[],rsv[];        // 计算用缓冲区
double p=0,p1=0;
double f=0,f1=0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,KBuffer,INDICATOR_DATA);           // K线数值
   SetIndexBuffer(1,KColorBuffer,INDICATOR_COLOR_INDEX); // K线颜色索引
   SetIndexBuffer(2,DBuffer,INDICATOR_DATA);           // D线数值
   SetIndexBuffer(3,DColorBuffer,INDICATOR_COLOR_INDEX); // D线颜色索引
   SetIndexBuffer(4,JBuffer,INDICATOR_DATA);           // J线数值
   SetIndexBuffer(5,JColorBuffer,INDICATOR_COLOR_INDEX); // J线颜色索引
   SetIndexBuffer(6,Level80Buffer,INDICATOR_DATA);     // 动态80水平线
   SetIndexBuffer(7,Level20Buffer,INDICATOR_DATA);     // 动态20水平线
   SetIndexBuffer(8,llv,INDICATOR_CALCULATIONS);       // 最低价计算
   SetIndexBuffer(9,hhv,INDICATOR_CALCULATIONS);       // 最高价计算
   SetIndexBuffer(10,rsv,INDICATOR_CALCULATIONS);      // RSV计算
   
   ArraySetAsSeries(KBuffer,true);      // K线数值时间序列
   ArraySetAsSeries(KColorBuffer,true); // K线颜色时间序列
   ArraySetAsSeries(DBuffer,true);      // D线数值时间序列
   ArraySetAsSeries(DColorBuffer,true); // D线颜色时间序列
   ArraySetAsSeries(JBuffer,true);      // J线数值时间序列
   ArraySetAsSeries(JColorBuffer,true); // J线颜色时间序列
   ArraySetAsSeries(Level80Buffer,true); // 动态80水平线时间序列
   ArraySetAsSeries(Level20Buffer,true); // 动态20水平线时间序列
   ArraySetAsSeries(llv,true);          // 最低价时间序列
   ArraySetAsSeries(hhv,true);          // 最高价时间序列
   ArraySetAsSeries(rsv,true);          // RSV时间序列   
      
   for(int i=0;i<11;i++)  // 现在有11个缓冲区
     {       
      PlotIndexSetInteger(i,PLOT_DRAW_BEGIN,N+M1+M2);
     }

   
   string name = "KDJ（升级版)";
   IndicatorSetString(INDICATOR_SHORTNAME,name);
   IndicatorSetInteger(INDICATOR_DIGITS,2);   

   if(N<=0||M1<=0||M2<=0) return(INIT_FAILED);
   
   p = 1.0/M1;   p1 = 1-p;
   f = 1.0/M2;   f1 = 1-f;   
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---   
   int i,limit=0;
   if(rates_total<=0)return(0);
   if(prev_calculated<=0)limit=rates_total-1;
   else limit = rates_total - prev_calculated +1;

   ArraySetAsSeries(low,true);   
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(close,true);

   for(i=limit; i>=0; i--)
     {
      llv[i]=0; hhv[i]=0;
      if(i>rates_total-N) continue;
      int shift = iLowest(NULL,0,MODE_LOW,N,i);
      llv[i] =  low[shift];
      shift = iHighest(NULL,0,MODE_HIGH,N,i);
      hhv[i] = high[shift];
     }
   for(i=limit; i>=0; i--)
     {
      rsv[i] = 0;
      if(hhv[i]>0 && llv[i]>0 && (hhv[i]-llv[i])!=0)
         rsv[i] = (close[i]-llv[i])/(hhv[i]-llv[i])*100;
     }

   for(i=limit; i>=0; i--)
     {
      if(i==rates_total-1) 
        {
         KBuffer[i]=0;
         KColorBuffer[i]=0;  // 初始颜色为白色
        }
      else
        {
         KBuffer[i] = rsv[i]*p + KBuffer[i+1]*p1;
        }
     }
   
   // 单独设置K线颜色，避免提前变色
   for(i=limit; i>=0; i--)
     {
      if(i>=rates_total-1) continue;  // 跳过最后一根K线，避免数组越界
      
      // 根据K值设置颜色
      // 对于最新的K线（i=0），直接根据当前值设置颜色
      if(i == 0)
        {
         if(KBuffer[i] > OverBought)
           KColorBuffer[i] = 1;      // 红色：超买区域
         else if(KBuffer[i] < OverSold)
           KColorBuffer[i] = 2;      // 绿色：超卖区域
         else
           KColorBuffer[i] = 0;      // 白色：正常区域
        }
      // 对于历史K线，检查当前值和前一个值，只有完全进入区域才变色
      else if(i >= 1)
        {
         if(KBuffer[i] > OverBought && KBuffer[i+1] > OverBought)
            KColorBuffer[i] = 1;      // 红色：完全在超买区域
          else if(KBuffer[i] < OverSold && KBuffer[i+1] < OverSold)
            KColorBuffer[i] = 2;      // 绿色：完全在超卖区域
         else
           KColorBuffer[i] = 0;      // 白色：正常区域或穿越边界
        }
     }

   for(i=limit; i>=0; i--)
     {
      if(i==rates_total-1) 
        {
         DBuffer[i]=0;
         DColorBuffer[i]=0;  // 初始颜色为金色
        }
      else
        {
         DBuffer[i] = KBuffer[i]*f + DBuffer[i+1]*f1;
        }
     }
   
   // 单独设置D线颜色，避免提前变色
   for(i=limit; i>=0; i--)
     {
      if(i>=rates_total-1) continue;  // 跳过最后一根K线，避免数组越界
      
      // 根据D值设置颜色
      // 对于最新的K线（i=0），直接根据当前值设置颜色
      if(i == 0)
        {
         if(DBuffer[i] > OverBought)
           DColorBuffer[i] = 1;      // 红色：超买区域
         else if(DBuffer[i] < OverSold)
           DColorBuffer[i] = 2;      // 绿色：超卖区域
         else
           DColorBuffer[i] = 0;      // 金色：正常区域
        }
      // 对于历史K线，检查当前值和前一个值，只有完全进入区域才变色
      else if(i >= 1)
        {
         if(DBuffer[i] > OverBought && DBuffer[i+1] > OverBought)
            DColorBuffer[i] = 1;      // 红色：完全在超买区域
          else if(DBuffer[i] < OverSold && DBuffer[i+1] < OverSold)
            DColorBuffer[i] = 2;      // 绿色：完全在超卖区域
         else
           DColorBuffer[i] = 0;      // 金色：正常区域或穿越边界
        }
     }

   for(i=limit; i>=0; i--)
     {
      JBuffer[i] = 3*KBuffer[i] - 2*DBuffer[i];
     }
   
   // 单独设置J线颜色，避免提前变色
   for(i=limit; i>=0; i--)
     {
      if(i>=rates_total-1) continue;  // 跳过最后一根K线，避免数组越界
      
      // 根据J值设置颜色
      // 对于最新的K线（i=0），直接根据当前值设置颜色
      if(i == 0)
        {
         if(JBuffer[i] > OverBought)
           JColorBuffer[i] = 1;      // 红色：超买区域
         else if(JBuffer[i] < OverSold)
           JColorBuffer[i] = 2;      // 绿色：超卖区域
         else
           JColorBuffer[i] = 0;      // 紫色：正常区域
        }
      // 对于历史K线，检查当前值和前一个值，只有完全进入区域才变色
      else if(i >= 1)
        {
         if(JBuffer[i] > OverBought && JBuffer[i+1] > OverBought)
            JColorBuffer[i] = 1;      // 红色：完全在超买区域
          else if(JBuffer[i] < OverSold && JBuffer[i+1] < OverSold)
            JColorBuffer[i] = 2;      // 绿色：完全在超卖区域
         else
           JColorBuffer[i] = 0;      // 紫色：正常区域或穿越边界
        }
     }
   
   // 设置动态80水平线：在K、D、J线超过80的位置及前后2根K线显示橙色实线
    for(i=limit; i>=0; i--)
      {
       Level80Buffer[i] = EMPTY_VALUE;  // 默认不显示线条
       
       // 检查当前位置及前后2根K线范围内是否有任何线超过80
       bool show80 = false;
       for(int j = i-2; j <= i+2; j++)
         {
          if(j >= 0 && j < rates_total)  // 确保索引在有效范围内
            {
             if(KBuffer[j] > OverBought || DBuffer[j] > OverBought || JBuffer[j] > OverBought)
               {
                show80 = true;
                break;
               }
            }
         }
       
       if(show80)
         {
          Level80Buffer[i] = OverBought;  // 显示超买水平线（延迟效果）
         }
      }
    
    // 设置动态20水平线：在K、D、J线低于20的位置及前后2根K线显示青色实线
    for(i=limit; i>=0; i--)
      {
       Level20Buffer[i] = EMPTY_VALUE;  // 默认不显示线条
       
       // 检查当前位置及前后2根K线范围内是否有任何线低于20
       bool show20 = false;
       for(int j = i-2; j <= i+2; j++)
         {
          if(j >= 0 && j < rates_total)  // 确保索引在有效范围内
            {
             if(KBuffer[j] < OverSold || DBuffer[j] < OverSold || JBuffer[j] < OverSold)
               {
                show20 = true;
                break;
               }
            }
         }
       
       if(show20)
         {
          Level20Buffer[i] = OverSold;  // 显示超卖水平线（延迟效果）
         }
      }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
